/*
 * bpfyfilter.{cc,hh} -- BPFy type filter
 * Eddie Kohler
 *
 * Copyright (c) 2007 Regents of the University of California
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, subject to the conditions
 * listed in the Click LICENSE file. These conditions include: you must
 * preserve this copyright notice, and you cannot mention the copyright
 * holders in advertising related to the Software without their permission.
 * The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This
 * notice is a summary of the Click LICENSE file; the license in that file is
 * legally binding.
 */

#include <click/config.h>
#include "crapfilter3.hh"
#include <click/glue.hh>
#include <click/error.hh>
#include <click/confparse.hh>
#include <click/straccum.hh>
#include <clicknet/ip.h>
#include <clicknet/udp.h>
#include <click/hashmap.hh>
#include <click/integers.hh>
#include <click/nameinfo.hh>
CLICK_DECLS

BpfyFilter::BpfyFilter()
{
}

BpfyFilter::~BpfyFilter()
{
}

int
BpfyFilter::initialize(ErrorHandler *)
{
    _insn.push_back(Insn(BPF_LD|BPF_B|BPF_ABS, 0, 0, 9)); // 15
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 19-16, 1, 0x84)); // 16
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 19-17, 1, 0x6)); // 17
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 19-18, 30-18, 0x11)); // 18
    _insn.push_back(Insn(BPF_LD|BPF_H|BPF_ABS, 0, 0, 6)); // 19
    _insn.push_back(Insn(BPF_JMP|BPF_K|BPF_JSET, 30-20, 1, 0x1fff)); // 20
    _insn.push_back(Insn(BPF_LDX|BPF_B|BPF_MSH, 0, 0, 0)); // 21
    _insn.push_back(Insn(BPF_LD|BPF_H|BPF_IND, 0, 0, 0)); // 22
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-23, 1, 0x51)); // 23
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-24, 1, 0x1e)); // 24
    
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-25, 1, 0x32)); // 25
    _insn.push_back(Insn(BPF_LD|BPF_H|BPF_IND, 0, 0, 2)); // 26
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-27, 1, 0x51)); // 27
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-28, 1, 0x1e)); // 28
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-29, 1, 0x32)); // 29
    _insn.push_back(Insn(BPF_LD|BPF_W|BPF_ABS, 0, 0, 12)); // 30
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-31, 1, 0x01000002)); // 31
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-32, 1, 0x121a0409)); // 32
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-33, 1, 0x09040301)); // 33
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-34, 1, 0x05040108)); // 34
    
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-35, 1, 0x09010102)); // 35
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-36, 1, 0x3001032b)); // 36
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-37, 1, 0x0a04022b)); // 37
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-38, 1, 0x2b5e0103)); // 38
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-39, 1, 0x66310204)); // 39
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-40, 1, 0xc931275c)); // 40
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-41, 1, 0x28282828)); // 41
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-42, 1, 0x29292929)); // 42
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-43, 1, 0x2a2a2a2a)); // 43
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-44, 1, 0x2b2b2b2b)); // 44
    
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-45, 1, 0x2c2c2c2c)); // 45 
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-46, 1, 0x2d2d2d2d)); // 46
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-47, 1, 0x2e2e2e2e)); // 47
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-48, 1, 0x2f2f2f2f)); // 48
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-49, 1, 0x30303030)); // 49
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-50, 1, 0x31313131)); // 50
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-51, 1, 0x32323232)); // 51
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-52, 1, 0x33333333)); // 52
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-53, 1, 0x34343434)); // 53
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-54, 1, 0x35353535)); // 54
    
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-55, 1, 0x36363636)); // 55
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-56, 1, 0x37373737)); // 56
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-57, 1, 0x38383838)); // 57
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-58, 1, 0x394b4b4b)); // 58
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-59, 1, 0x4c4c4c4c)); // 59
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-60, 1, 0xb1b1b1b1)); // 60
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-61, 1, 0xc1c1c1c1)); // 61
    _insn.push_back(Insn(BPF_LD|BPF_W|BPF_ABS, 0, 0, 16)); // 62
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-63, 100-63, 0x1000002)); // 63
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 2, 1, 0x806)); // 64
    
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 1, 131-65, 0x8035)); // 65
    _insn.push_back(Insn(BPF_LD|BPF_W|BPF_ABS, 0, 0, 14)); // 66
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-67, 1, 0x01000002)); // 67
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-68, 1, 0x121a0409)); // 68
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-69, 1, 0x09040301)); // 69
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-70, 1, 0x05040108)); // 70
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-71, 1, 0x09010102)); // 71
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-72, 1, 0x3001032b)); // 72
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-73, 1, 0x0a04022b)); // 73
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-74, 1, 0x2b5e0103)); // 74
    
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-75, 1, 0x66310204)); // 75
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-76, 1, 0xc931275c)); // 76
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-77, 1, 0x28282828)); // 77
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-78, 1, 0x29292929)); // 78
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-79, 1, 0x2a2a2a2a)); // 79
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-80, 1, 0x2b2b2b2b)); // 80
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-81, 1, 0x2c2c2c2c)); // 81 
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-82, 1, 0x2d2d2d2d)); // 82
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-83, 1, 0x2e2e2e2e)); // 83
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-84, 1, 0x2f2f2f2f)); // 84
    
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-85, 1, 0x30303030)); // 85
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-86, 1, 0x31313131)); // 86
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-87, 1, 0x32323232)); // 87
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-88, 1, 0x33333333)); // 88
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-89, 1, 0x34343434)); // 89
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-90, 1, 0x35353535)); // 90
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-91, 1, 0x36363636)); // 91
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-92, 1, 0x37373737)); // 92
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-93, 1, 0x38383838)); // 93
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-94, 1, 0x394b4b4b)); // 94
    
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-95, 1, 0x4c4c4c4c)); // 95
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-96, 1, 0xb1b1b1b1)); // 96
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-97, 1, 0xc1c1c1c1)); // 97
    _insn.push_back(Insn(BPF_LD|BPF_W|BPF_ABS, 0, 0, 24)); // 98
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-99, 1, 0x01000002)); // 99
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-100, 1, 0x121a0409)); // 100
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-101, 1, 0x09040301)); // 101
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-102, 1, 0x05040108)); // 102
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-103, 1, 0x09010102)); // 103
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-104, 1, 0x3001032b)); // 104
    
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-105, 1, 0x0a04022b)); // 105
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-106, 1, 0x2b5e0103)); // 106
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-107, 1, 0x66310204)); // 107
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-108, 1, 0xc931275c)); // 108
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-109, 1, 0x28282828)); // 109
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-110, 1, 0x29292929)); // 110
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-111, 1, 0x2a2a2a2a)); // 111
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-112, 1, 0x2b2b2b2b)); // 112
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-113, 1, 0x2c2c2c2c)); // 113
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-114, 1, 0x2d2d2d2d)); // 114
    
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-115, 1, 0x2e2e2e2e)); // 115
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-116, 1, 0x2f2f2f2f)); // 116
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-117, 1, 0x30303030)); // 117
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-118, 1, 0x31313131)); // 118
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-119, 1, 0x32323232)); // 119
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-120, 1, 0x33333333)); // 120
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-121, 1, 0x34343434)); // 121
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-122, 1, 0x35353535)); // 122
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-123, 1, 0x36363636)); // 123
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-124, 1, 0x37373737)); // 124
    
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-125, 1, 0x38383838)); // 125
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-126, 1, 0x394b4b4b)); // 126
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-127, 1, 0x4c4c4c4c)); // 127
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-128, 1, 0xb1b1b1b1)); // 128
    _insn.push_back(Insn(BPF_JMP|BPF_JEQ|BPF_K, 130-129, 2, 0xc1c1c1c1)); // 129
    _insn.push_back(Insn(BPF_RET|BPF_K, 0, 0, 1)); // 130
    _insn.push_back(Insn(BPF_RET|BPF_K, 0, 0, 0)); // 131
    return 0;
}

static inline const void *header_pointer(const Packet *p, int offset,
				   int len, void *)
{
	if (p->network_length() - offset >= len)
	    return p->network_header() + offset;
	
	return NULL;
}

static inline const void *load_pointer(Packet *p, int k,
                                 unsigned int size, void *buffer)
{
	if (k >= 0)
		return header_pointer(p, k, size, buffer);
	else
	    return NULL;
}

#define get_unaligned(ptr) (*(ptr))

void
BpfyFilter::push(int, Packet *p)
{
	const void *ptr;
	uint32_t A = 0;			/* Accumulator */
	uint32_t X = 0;			/* Index Register */
	uint32_t mem[BPF_MEMWORDS];	/* Scratch Memory Store */
	uint32_t tmp;
	int k;
	int pc;
	Insn *filter = _insn.begin();
	int flen = _insn.size();

	/*
	 * Process array of filter instructions.
	 */
	for (pc = 0; pc < flen; pc++) {
		Insn *fentry = &filter[pc];
			
		switch (fentry->code) {
		case BPF_ALU|BPF_ADD|BPF_X:
			A += X;
			continue;
		case BPF_ALU|BPF_ADD|BPF_K:
			A += fentry->k;
			continue;
		case BPF_ALU|BPF_SUB|BPF_X:
			A -= X;
			continue;
		case BPF_ALU|BPF_SUB|BPF_K:
			A -= fentry->k;
			continue;
		case BPF_ALU|BPF_MUL|BPF_X:
			A *= X;
			continue;
		case BPF_ALU|BPF_MUL|BPF_K:
			A *= fentry->k;
			continue;
		case BPF_ALU|BPF_DIV|BPF_X:
			if (X == 0) {
				p->kill();
				return;
			}
			A /= X;
			continue;
		case BPF_ALU|BPF_DIV|BPF_K:
			A /= fentry->k;
			continue;
		case BPF_ALU|BPF_AND|BPF_X:
			A &= X;
			continue;
		case BPF_ALU|BPF_AND|BPF_K:
			A &= fentry->k;
			continue;
		case BPF_ALU|BPF_OR|BPF_X:
			A |= X;
			continue;
		case BPF_ALU|BPF_OR|BPF_K:
			A |= fentry->k;
			continue;
		case BPF_ALU|BPF_LSH|BPF_X:
			A <<= X;
			continue;
		case BPF_ALU|BPF_LSH|BPF_K:
			A <<= fentry->k;
			continue;
		case BPF_ALU|BPF_RSH|BPF_X:
			A >>= X;
			continue;
		case BPF_ALU|BPF_RSH|BPF_K:
			A >>= fentry->k;
			continue;
		case BPF_ALU|BPF_NEG:
			A = -A;
			continue;
		case BPF_JMP|BPF_JA:
			pc += fentry->k;
			continue;
		case BPF_JMP|BPF_JGT|BPF_K:
			pc += (A > fentry->k) ? fentry->jt : fentry->jf;
			continue;
		case BPF_JMP|BPF_JGE|BPF_K:
			pc += (A >= fentry->k) ? fentry->jt : fentry->jf;
			continue;
		case BPF_JMP|BPF_JEQ|BPF_K:
			pc += (A == fentry->k) ? fentry->jt : fentry->jf;
			continue;
		case BPF_JMP|BPF_JSET|BPF_K:
			pc += (A & fentry->k) ? fentry->jt : fentry->jf;
			continue;
		case BPF_JMP|BPF_JGT|BPF_X:
			pc += (A > X) ? fentry->jt : fentry->jf;
			continue;
		case BPF_JMP|BPF_JGE|BPF_X:
			pc += (A >= X) ? fentry->jt : fentry->jf;
			continue;
		case BPF_JMP|BPF_JEQ|BPF_X:
			pc += (A == X) ? fentry->jt : fentry->jf;
			continue;
		case BPF_JMP|BPF_JSET|BPF_X:
			pc += (A & X) ? fentry->jt : fentry->jf;
			continue;
		case BPF_LD|BPF_W|BPF_ABS:
			k = fentry->k;
load_w:
			ptr = load_pointer(p, k, 4, &tmp);
			if (ptr != NULL) {
				A = ntohl(get_unaligned((uint32_t *)ptr));
				continue;
			}
			break;
		case BPF_LD|BPF_H|BPF_ABS:
			k = fentry->k;
load_h:
			ptr = load_pointer(p, k, 2, &tmp);
			if (ptr != NULL) {
				A = ntohs(get_unaligned((uint16_t *)ptr));
				continue;
			}
			break;
		case BPF_LD|BPF_B|BPF_ABS:
			k = fentry->k;
load_b:
			ptr = load_pointer(p, k, 1, &tmp);
			if (ptr != NULL) {
				A = *(uint8_t *)ptr;
				continue;
			}
			break;
		case BPF_LD|BPF_W|BPF_LEN:
			A = p->length();
			continue;
		case BPF_LDX|BPF_W|BPF_LEN:
			X = p->length();
			continue;
		case BPF_LD|BPF_W|BPF_IND:
			k = X + fentry->k;
			goto load_w;
		case BPF_LD|BPF_H|BPF_IND:
			k = X + fentry->k;
			goto load_h;
		case BPF_LD|BPF_B|BPF_IND:
			k = X + fentry->k;
			goto load_b;
		case BPF_LDX|BPF_B|BPF_MSH:
			ptr = load_pointer(p, fentry->k, 1, &tmp);
			if (ptr != NULL) {
				X = (*(uint8_t *)ptr & 0xf) << 2;
				continue;
			}
			p->kill();
			return;
		case BPF_LD|BPF_IMM:
			A = fentry->k;
			continue;
		case BPF_LDX|BPF_IMM:
			X = fentry->k;
			continue;
		case BPF_LD|BPF_MEM:
			A = mem[fentry->k];
			continue;
		case BPF_LDX|BPF_MEM:
			X = mem[fentry->k];
			continue;
		case BPF_MISC|BPF_TAX:
			X = A;
			continue;
		case BPF_MISC|BPF_TXA:
			A = X;
			continue;
		case BPF_RET|BPF_K:
			checked_output_push(fentry->k, p);
			return;
		case BPF_RET|BPF_A:
			checked_output_push(A, p);
			return;
		case BPF_ST:
			mem[fentry->k] = A;
			continue;
		case BPF_STX:
			mem[fentry->k] = X;
			continue;
		default:
			p->kill();
			return;
		}
	}
}

#include <click/vector.cc>
CLICK_ENDDECLS
EXPORT_ELEMENT(BpfyFilter)
